using System;
using System.Threading;

namespace DirectoryWatcher
{
    /// <summary>
    /// Semaphore implementation that includes a resource constraint:  executing functions are 
    /// allowed to enter the critical section as long as there is at least one resource available,
    /// but will have to wait until resources are free if all of them are currently in use.
    /// </summary>
    public class CountingSemaphore
    {
        /// <summary>
        /// Resource limit for the semaphore
        /// </summary>
        private uint count;

        /// <summary>
        /// Default constructor; resource count is 1, meaning the class will act like a standard,
        /// binary semaphore.
        /// </summary>
		public CountingSemaphore() : this(1) 
		{
		}

        /// <summary>
        /// Constructor that allows you to set the number of resources that should be available for 
        /// consumption.
        /// </summary>
        /// <param name="count">
        /// Number of resources that should be available for consumption.
        /// </param>
		public CountingSemaphore(uint count) 
		{
			this.count = count;
		}

        /// <summary>
        /// Function that should be called when leaving the critical section; frees up one 
        /// resource.
        /// </summary>
		public void AddOne()
        { 
            V();
        }

        /// <summary>
        /// Function that should be called when entering a critical section; claims one resource or
        /// waits if no resources are available.
        /// </summary>
		public void WaitOne()
        {
            P(); 
        }

        /// <summary>
        /// Function that should be called when entering a critical section; claims one resource or
        /// waits if no resources are available.
        /// </summary>
		public void P() 
		{
			lock(this) 
			{
				while (count <= 0)
                    Monitor.Wait(this, Timeout.Infinite);

				count--;
			}
		}

        /// <summary>
        /// Function that should be called when leaving the critical section; frees up one 
        /// resource.
        /// </summary>
		public void V() 
		{
			lock(this) 
			{
				count++;
				Monitor.Pulse(this);
			}
		}
    }
}
